<?php

/**
 * 观察者（发布-订阅、模型-视图、源-监听器、从属者）模式（对象间的联动）
 * 
 * 概述：定义对象间一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都得到通知并被自动更新。
 * 
 * 类型：行为型模式
 * 
 * 角色：
 *  1，目标。目标又称为主题，它是指被观察的对象。
 *  2，具体目标。
 *  3，观察者。
 *  4，具体观察者。
 *  
 * 总结：观察者模式是一种使用频率非常高的设计模式，无论是移动应用、Web应用或者桌面应用，
 *  	观察者模式几乎无处不在，它为实现对象之间的联动提供了一套完整的解决方案，凡是涉及到
 *  	一对一或者一对多的对象交互场景都可以使用观察者模式。观察者模式广泛应用于各种编程语言
 *  	的GUI事件处理的实现，在基于事件的XML解析技术（如SAX2）以及Web事件处理中也都使用了观察者模式。
 * 
 * 优点：
 *  1，观察者模式可以实现表示层和数据逻辑层的分离，定义了稳定的消息更新传递机制，并抽象了更新接口，使得
 *     可以有各种各样不同的表示层充当具体观察者角色。
 *  2，观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合，无须
 *     了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起，因此它们可以属于不同的抽象化层次。
 *  3，观察者模式支持广播通信，观察目标会向所有已注册的观察者对象发送通知，简化了一对多系统设计的难度。
 *  4，观察者模式满足“开闭原则”的要求，增加新的具体观察者无须修改原有系统代码，在具体观察者与观察目标之间
 *     不存在关联关系的情况下，增加新的观察目标也很方便。
 * 
 * 缺点：
 * 1，如果一个观察目标对象有很多直接和间接观察者，将所有的观察者都通知到会花费很多时间。
 * 2，如果在观察者和观察目标之间存在循环依赖，观察目标会触发它们之间进行循环调用，可能导致系统崩溃。
 * 3，观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的，而仅仅只是知道观察目标发生了变化。
 *
 * 适应场景：
 *  1，一个抽象模型有两个方面，其中一个方面依赖于另一个方面，将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。
 *  2，一个对象的改变将导致一个或多个其他对象也发生改变，而并不知道具体有多少对象将发生改变，也不知道这些对象是谁。
 *  3， 需要在系统中创建一个触发链，A对象的行为将影响B对象，B对象的行为将影响C对象……，可以使用观察者模式创建一种链式触发机制。
 * 
 * 其它：
 *  在php的SPL支持观察者模式，SPL 提供了 SplSubject 和 SplObserver 接口
 */

/**
 * 目标
 */
interface Subject
{
	/**
	 * 添加观察者
	 */
	public function attach(Observer $observer);
	
	/**
	 * 删除观察者
	 */
	public function detach(Observer $observer);
	
	/**
	 * 通知
	 */
	public function notify();
}

/**
 * 具体目标
 */
class ConcreteSubject implements Subject
{
	private $_observers;
	
	public function __construct()
	{
		$this->_observers = array();
	}
	
	public function attach(Observer $observer)
	{
		$this->_observers[] = $observer;
	}
	
	public function detach(Observer $observer)
	{
		$index = array_search($observer, $this->_observers);
		if ($index !== false) {
			unset($this->_observers[$index]);
		}
	}
	
	public function notify()
	{
		foreach ($this->_observers as $observer) {
			$observer->update();
		}
	}
}

/**
 * 抽象观察者
 */
interface Observer
{
	/**
	 * 更新方法
	 */
	public function update();
}

/**
 * 具体观察者
 */
class ConcreteObserverA implements Observer
{
	private $_name;
	
	public function __construct($name)
	{
		$this->_name = $name;
	}
	
	public function update()
	{
		echo 'A类观察者发生变化。<br/>';
	}
}

class ConcreteObserverB implements Observer
{
	private $_name;
	
	public function __construct($name)
	{
		$this->_name = $name;
	}
	
	public function update()
	{
		echo 'B类观察者发生变化。<br/>';
	}
}

class Client
{
	public static function main()
	{
		$subject = new ConcreteSubject();
		
		//添加观察者
		$observer1 = new ConcreteObserverA('张三');
		$observer2 = new ConcreteObserverA('李四');
		$observer3 = new ConcreteObserverB('王五');
		
		$subject->attach($observer1);
		$subject->attach($observer2);
		$subject->attach($observer3);
		
		//目标状态改变时，发送通知
		echo '目标对象发生变化。<br/>';
		$subject->notify();
	}
}

Client::main();